#
# This file is part of uscp, an open-source cross-platform package tool
# 2008  Rafael Sachetto
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Contact e-mail: Rafael Sachetto <rsachetto@gmail.com>
# Program URL   : http://code.google.com/p/uspc/

#USPC library imports
from maindialogui import Ui_MainDialog
from messages import *
from downloadthread import DownloadThread
from getpackages import *
from aboutdialog import AboutDialog
from defines import VERSION, ARCH_INDEX, CUR_RELEASE_INDEX, RELEASES, DISTS, ARCHS
from optionsdialog import OptionsDialog


#PyQt4 imports
from PyQt4.QtCore import Qt, QSettings, QVariant, QDir, \
SIGNAL, SLOT, QString

from PyQt4.QtGui import QMainWindow, QFileDialog, \
QApplication, QTreeWidgetItem, \
QMessageBox, QAction, QMenu, QCursor, qApp, QCompleter, QDirModel

class MainDialog(QMainWindow, Ui_MainDialog):

    def __init__(self, parent=None, args=None):

        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.createConnections()
        
        self.downloadThreadList = None
        self.listThread = None
        self.useCache = False
        self.downloadDefault = False
        self.numThreads = 4
        self.overwriteAll = False
        self.listing = False
        self.downloading = False
        
        self.initAll()


    def initAll(self):

        """
        Important Variables initialization
        """
        
        self.completer = QCompleter(self)
        dirModel = QDirModel(self.completer)
        self.completer.setModel(dirModel)
        self.outputFolderEdit.setCompleter(self.completer)

        self.distributionComboBox.insertItems(0, DISTS)

        self.archComboBox.insertItems(0, ARCHS)
        self.resizeTreeView()
        self.bar = self.statusBar()
        self.bar.showMessage("Ubuntu Package Crawler")

        self.downloadProgressBar.setValue(0)

        self.readSettings()

    def createConnections(self):

        """Create all the connections needed by the app."""

        self.connect(self.quitButton, SIGNAL("clicked()"), self.closeOrCancel)
        self.connect(self.listDepsButton, SIGNAL("clicked()"), self.listDependencies)
        self.connect(self.browseButton, SIGNAL("clicked()"), self.browseDestinationFolder)
        self.connect(self.downloadButton, SIGNAL("clicked()"), self.downloadAll)

        self.connect(self.addListButton, SIGNAL("clicked()"), self.addPackageToList)
        self.connect(self.clearListButton, SIGNAL("clicked()"), self.removeAllFromList)
        self.connect(self.downloadSelectedButton, SIGNAL("clicked()"), self.downloadSelected)
        self.connect(self.filesTreeView, SIGNAL("customContextMenuRequested (QPoint)"), self.showMenu)
        self.connect(self.actionAbout_USPC, SIGNAL("triggered()"), self.about)
        self.connect(self.actionAbout_Qt, SIGNAL("triggered()"), qApp, SLOT("aboutQt()"))

        self.connect(self.useCachecheckBox, SIGNAL("stateChanged (int)"), self.verify);
        self.connect(self.downloadDefaultcheckBox, SIGNAL("stateChanged (int)"), self.verify);
        
        self.connect(self.actionOptions, SIGNAL("triggered()"), self.setOptions)

        self.connect(self.distributionComboBox,
                     SIGNAL("currentIndexChanged (const QString &)"),
                     self.changeReleases)

    def changeReleases(self, distribution):

        releases_list = RELEASES[str(distribution)]

        if distribution == "Ubuntu":
            self.releaseComboBox.clear()
            self.releaseComboBox.insertItems(0, releases_list)
            
        elif distribution == "Debian":
            self.releaseComboBox.clear()
            self.releaseComboBox.insertItems(0, releases_list)


    def updateList(self, pack, pack_url, size, release, arch):

        """
        Receives the package names from the list threads and update
        the package list.
        """

        print "Updating package list"
        itemList = []
        itemList.append(pack)
        itemList.append("%.0lf" % (size))
        itemList.append(release)
        itemList.append(arch)
        itemList.append(QApplication.translate("MainDialog", "Not Downloaded yet", None, QApplication.UnicodeUTF8))
        itemList.append(pack_url)
        item = QTreeWidgetItem(itemList)
        self.filesTreeView.addTopLevelItem(item)

    def addPackageToList(self):

        """
        Called when the add to list button is clicked.
        Add only one specific package to the list.
        """

        #Starts the list thread
        distribution = self.distributionComboBox.currentText().simplified()

        if distribution == "Ubuntu":
            self.listThread = ListUbuntuPackages(self)
        elif distribution == "Debian":
            self.listThread = ListDebianPackages(self)

        self.connect(self.listThread,
                     SIGNAL("updateList(const QString &, \
                     const QString &, float, \
                     const QString &, const QString &)"),
                     self.updateList,
                     Qt.QueuedConnection)


        self.connect(self.listThread, SIGNAL("endFetchingList()"),
                     self.endFetchingList, Qt.QueuedConnection)

        self.connect(self.listThread, SIGNAL("updateBar()"),
                     self.updateBar, Qt.QueuedConnection)

        self.connect(self.listThread,
                     SIGNAL("error(const QString &)"),
                     self.errorListing, Qt.QueuedConnection)



        package = self.packageEdit.text()

        if not self.verifyPackageName(package):
            return

        arch = self.archComboBox.currentText()
        release = self.releaseComboBox.currentText().split(" ")[0]

        self.setCursor(Qt.WaitCursor)

        self.changeWidgetsStatus(status = False)

        self.addListButton.setText("Fetching ...")

        self.listThread.enqueueFile(package, release, arch, listDeps = False, useCache = self.useCache,
                                    downloadDefault = True)

    def updateDownloadStatus(self, url, prog, bytes):

        """
        Update the progress bar with the current download status
        """

        count = self.filesTreeView.topLevelItemCount()

        if int(prog) < 100:
            progress = int(prog)
        else:
            progress = 100

        for i in xrange(count):
            item = self.filesTreeView.topLevelItem(i)
            if((item.text(5) == url)):
                item.setText(4, str(progress) + "%")
                self.downloadProgressBar.setValue(self.downloadProgressBar.value() +  bytes)
                break

    def endFetchingList(self):

        """
        Called when all packages are fetched from the web site
        """

        print "endFetchingList"

        self.downloadProgressBar.setRange(0, 1)
        self.downloadProgressBar.setValue(0)
        self.listDepsButton.setEnabled(True)
        self.addListButton.setEnabled(True)
        self.browseButton.setEnabled(True)
        self.packageEdit.setEnabled(True)
        self.outputFolderEdit.setEnabled(True)
        self.releaseComboBox.setEnabled(True)
        self.archComboBox.setEnabled(True)
        self.distributionComboBox.setEnabled(True)

        self.listDepsButton.setText("&List Dependencies")
        self.addListButton.setText("&Add to List")
        self.resizeTreeView()

        if self.filesTreeView.topLevelItemCount() > 0:
            self.downloadButton.setEnabled(True)
            self.downloadSelectedButton.setEnabled(True)
            self.clearListButton.setEnabled(True)

        self.setCursor(Qt.ArrowCursor)
        self.listing = False
        self.quitButton.setText(QApplication.translate("MainDialog", "Quit", None,
                                                       QApplication.UnicodeUTF8))

    def verifyPackageName(self, package):

        """
        Verify if the user provided a package name on the interface.
        """

        if(not package):
            QMessageBox.warning(
                self, "USPC",
                QApplication.translate("MainDialog", "Please, provide a package name!", None, QApplication.UnicodeUTF8))
            return False
        return True

    def listDependencies(self):

        """
        List all the dependencies of a package and add then to the list.
        """

        #Starts the list thread
        distribution = self.distributionComboBox.currentText().simplified()

        if distribution == "Ubuntu":
            self.listThread = ListUbuntuPackages(self)
        elif distribution == "Debian":
            self.listThread = ListDebianPackages(self)

        self.connect(self.listThread,
                     SIGNAL("updateList(const QString &, \
                     const QString &, float, \
                     const QString &, const QString &)"),
                     self.updateList,
                     Qt.QueuedConnection)
                                             
        self.connect(self.listThread, SIGNAL("endFetchingList()"),
                     self.endFetchingList, Qt.QueuedConnection)

        self.connect(self.listThread, SIGNAL("updateBar()"),
                     self.updateBar, Qt.QueuedConnection)

        self.connect(self.listThread,
                     SIGNAL("error(const QString &)"),
                     self.errorListing, Qt.QueuedConnection)

        self.quitButton.setText(QApplication.translate("MainDialog",
                                                    "Cancel", None,
                                                     QApplication.UnicodeUTF8))
        self.listing = True

        package = self.packageEdit.text().simplified()

        if not self.verifyPackageName(package):
            return

        arch = self.archComboBox.currentText()
        release = self.releaseComboBox.currentText().split(" ")[0]

        self.setCursor(Qt.WaitCursor)

        self.changeWidgetsStatus(status = False)

        self.listDepsButton.setText("Fetching list...")
        self.listThread.enqueueFile(package, release, arch, listDeps = True, useCache = self.useCache,
                                    downloadDefault = self.downloadDefault)

        self.downloadProgressBar.setRange(0, 0)

        self.bar.showMessage("Listing dependencies for %s" % (package))

    def browseDestinationFolder(self):

        """
        Called when the browse destination button is cliked.
        Open a file dialog.
        """

        initialName = self.outputFolderEdit.text()

        if initialName.isEmpty():
            initialName = QDir.homePath()

        fileName = QFileDialog.getExistingDirectory(
                self,
                CHOOSE_DIR,
                initialName,
                QFileDialog.ShowDirsOnly)

        fileName = QDir.convertSeparators(fileName)

        if not fileName.isEmpty():
            self.outputFolderEdit.setText(fileName)

    def downloadSelected(self):

        """
        Download the selected packages from the Ubuntu web site.
        """

        print "downloadSelected"

        self.initDownloadThreads()

        self.downloading = True
        self.quitButton.setText(QApplication.translate("MainDialog", "Cancel",
                                                        None,
                                                       QApplication.UnicodeUTF8))

        selectedItems = self.filesTreeView.selectedItems();

        count = len(selectedItems)
        filesLength = 0

        if self.numThreads > count:
            numThreads = count
        else:
            numThreads = self.numThreads

        if count > 0:
            self.downloadProgressBar.setRange(0, len(selectedItems))
            self.setCursor(Qt.WaitCursor)
            self.changeWidgetsStatus(status = False)
            self.downloadProgressBar.setRange(0,
                            self.filesTreeView.topLevelItemCount())

            self.downloadProgressBar.setValue(0)

            destFolder = QDir(self.outputFolderEdit.text().simplified())

            if not destFolder.exists():
                msg = "The folder " + str(destFolder.absolutePath()) + " does not exists.Do you want to create it?"
                q = QMessageBox.question(
                             self,
                             QApplication.translate("MainDialog", "Create Folder? -- USPC", None, QApplication.UnicodeUTF8),
                             QApplication.translate("MainDialog", msg, None, QApplication.UnicodeUTF8),
                             QApplication.translate("MainDialog", "&Yes", None, QApplication.UnicodeUTF8),
                             QApplication.translate("MainDialog", "&No", None, QApplication.UnicodeUTF8) ,
                             QString(),
                             0,
                             1)

                if q == 0:
                    if (not destFolder.mkdir(destFolder.absolutePath())):
                        QMessageBox.warning(
                            self, "USPC",
                            QApplication.translate("MainDialog", "Unable to create target folder.", None, QApplication.UnicodeUTF8))
                        return
                elif q == 1:
                    return

            for i in xrange(numThreads):
                self.downloadThreadList[i].setDestFolder(destFolder)
                self.downloadThreadList[i].setOverwriteAll(False)

            roundRobin = 0

            for item in selectedItems:
                index = roundRobin % numThreads
                url, size = self.listThread.packages[(str(item.text(0)), str(item.text(2)), str(item.text(3)),)]
                self.downloadThreadList[index].enqueueFile(url, size)
                filesLength = filesLength + size
                roundRobin += 1

            self.downloadProgressBar.setRange(0, filesLength)
            self.downloadProgressBar.setValue(0)

        else:
            QMessageBox.warning(
                self, "USPC",
                QApplication.translate("MainDialog", "Please select at least one file to download", None, QApplication.UnicodeUTF8))

    def initDownloadThreads(self):

        "Initialize the download threads"

        self.downloadThreadList = []

        for i in xrange(self.numThreads):
            self.downloadThreadList.append(DownloadThread(parent = self, id = i))

        for t in self.downloadThreadList:
            self.connect(t, SIGNAL("updateDownloadStatus(const QString &, float, int)"), self.updateDownloadStatus, Qt.QueuedConnection)
            self.connect(t, SIGNAL("overwriteQuestion(const QString &, int)"), self.queryOverwrite, Qt.QueuedConnection)
            self.connect(t, SIGNAL("fileStatus(const QStringList &, const QString &)"), self.setFileStatus, Qt.QueuedConnection)


    def stopDownloadThreads(self):

        for t in self.downloadThreadList:
            t.terminate()


    def downloadAll(self):

        """
        Download all listed packages.
        """

        self.initDownloadThreads()

        self.downloading = True
        self.quitButton.setText(QApplication.translate("MainDialog", "Cancel", None,
                                                       QApplication.UnicodeUTF8))

        print "downloadAll"
        self.setCursor(Qt.WaitCursor)
        self.changeWidgetsStatus(status = False)

        filesLength = 0

        destFolder = QDir(self.outputFolderEdit.text().simplified())

        if not destFolder.exists():
            msg = "The folder " + str(destFolder.absolutePath()) + " does not exists.Do you want to create it?"
            q = QMessageBox.question(
                         self,
                         QApplication.translate("MainDialog", "Create Folder? -- USPC", None, QApplication.UnicodeUTF8),
                         QApplication.translate("MainDialog", msg, None, QApplication.UnicodeUTF8),
                         QApplication.translate("MainDialog", "&Yes", None, QApplication.UnicodeUTF8),
                         QApplication.translate("MainDialog", "&No", None, QApplication.UnicodeUTF8) ,
                         QString(),
                         0,
                         1)

            if q == 0:
                if (not destFolder.mkdir(destFolder.absolutePath())):
                    QMessageBox.warning(
                        self, "USPC",
                        QApplication.translate("MainDialog", "Unable to create target folder.", None, QApplication.UnicodeUTF8))
                    return
            elif q == 1:
                return

        item = None
        count = self.filesTreeView.topLevelItemCount()

        if self.numThreads > count:
            numThreads = count
        else:
            numThreads = self.numThreads

        if (count > 0):

            #initialize the threads do download in the rigth path
            for i in xrange(numThreads):
                self.downloadThreadList[i%numThreads].setDestFolder(destFolder)
                self.downloadThreadList[i].setOverwriteAll(False)

            #For each package we assign a thread to do the job in a Round Robin
            #way
            for i in xrange(0, count):
                #assign the filesTreeView.topLevelItem function to variable top
                #to avoid to many name resoutions
                top = self.filesTreeView.topLevelItem

                item = top(i)
                url, size = self.listThread.packages[(str(item.text(0)), str(item.text(2)), str(item.text(3)),)]
                self.downloadThreadList[i%numThreads].enqueueFile(url, size)
                filesLength = filesLength + size

            self.downloadProgressBar.setRange(0, filesLength)
            self.downloadProgressBar.setValue(0)
        else:
            QMessageBox.warning(
                self, "SPC",
                QApplication.translate("MainDialog", "Please add at least one file to download.", None, QApplication.UnicodeUTF8))


    def queryOverwrite(self, targetFile, id):

        """
        Asks the user the user if he/she wants to overried a alread downloaded file.
        """

        msg = "A file called " + str(targetFile) + " already exists. Do you want to overwrite it?"
        
        #result = QMessageBox.question(
                    #self,
                    #QApplication.translate("MainDialog","Overwrite File? -- SPC", None, QApplication.UnicodeUTF8),
                    #QApplication.translate("MainDialog",msg,None,QApplication.UnicodeUTF8),
                    #QApplication.translate("MainDialog","&Yes",None, QApplication.UnicodeUTF8),
                    #QApplication.translate("MainDialog","&No",None, QApplication.UnicodeUTF8),
                    #QApplication.translate("MainDialog","Yes to &All",None, QApplication.UnicodeUTF8),
                    #0,
                    #1)

        result = QMessageBox.question(
                    self,
                    QApplication.translate("MainDialog", "Overwrite File? -- SPC", None, QApplication.UnicodeUTF8),
                    QApplication.translate("MainDialog", msg, None, QApplication.UnicodeUTF8),
                    QApplication.translate("MainDialog", "&Yes", None, QApplication.UnicodeUTF8),
                    QApplication.translate("MainDialog", "&No", None, QApplication.UnicodeUTF8),
                    QString(),
                    0,
                    1)


        self.downloadThreadList[id].confirmOverwrite(result)
        

    def setFileStatus(self, fileData, status):

        """
        Set the status of a file on the download list.
        The status can be "Downloaded", "Failed to Download", "Skipped"
        """

        count = self.filesTreeView.topLevelItemCount()

        #search for the package whose status has to be updated
        for i in xrange(count):
            item = self.filesTreeView.topLevelItem(i)
            if item.text(5) == fileData[0]:
                item.setText(4, status)
                break

        #Finished all downloads. Update the interface.
        if self.downloadProgressBar.value() == self.downloadProgressBar.maximum():
            self.downloadsFinished()

    def downloadsFinished(self):
        self.changeWidgetsStatus(status = True)
        self.resizeTreeView()
        self.setCursor(Qt.ArrowCursor)
        self.overwriteAll = False
        self.quitButton.setText(QApplication.translate("MainDialog", "Quit", None,
                                                   QApplication.UnicodeUTF8))
        self.downloading = False

    def changeWidgetsStatus(self, status):

        self.downloadSelectedButton.setEnabled(status)
        self.downloadButton.setEnabled(status)
        self.listDepsButton.setEnabled(status)
        self.addListButton.setEnabled(status)
        self.clearListButton.setEnabled(status)
        self.browseButton.setEnabled(status)
        self.packageEdit.setEnabled(status)
        self.outputFolderEdit.setEnabled(status)
        self.releaseComboBox.setEnabled(status)
        self.archComboBox.setEnabled(status)
        self.distributionComboBox.setEnabled(status)

    def resizeTreeView(self):

        self.filesTreeView.resizeColumnToContents (0)
        self.filesTreeView.resizeColumnToContents (1)
        self.filesTreeView.resizeColumnToContents (2)
        self.filesTreeView.resizeColumnToContents (3)

    def removeSelectedFromList(self):

        """
        Remove selected items from the list and update the interface.
        """

        count = self.filesTreeView.topLevelItemCount()
        item = QTreeWidgetItem()
        topLevelItem = self.filesTreeView.topLevelItem
        packs = self.listThread.packages

        for i in xrange(count):
            item = topLevelItem(i)
            if item and item.isSelected():
                item = self.filesTreeView.takeTopLevelItem(i)
                key = (str(item.text(0)), str(item.text(2)), str(item.text(3)))
                del packs[key]

        if self.filesTreeView.topLevelItemCount() == 0:
            self.downloadButton.setEnabled(False)
            self.downloadSelectedButton.setEnabled(False);
            self.clearListButton.setEnabled(False)

    def removeAllFromList(self):

        """
        Remove all items from the list and update the interface.
        """

        self.filesTreeView.clear()
        self.downloadButton.setEnabled(False)
        self.downloadSelectedButton.setEnabled(False);
        self.clearListButton.setEnabled(False)

    def selectAllFromList(self):

        """
        Select all items from the list.
        """

        self.filesTreeView.selectAll()

    def about(self):

        """
        Show the about dialog.
        """

        about = AboutDialog();
        about.setVersion(QString(QApplication.translate("MainDialog", "Version: ", None, QApplication.UnicodeUTF8)) + VERSION)
        about.exec_();

    def showMenu(self, point):

        """
        Show a popup menu.
        """

        download = QAction(self)
        remove = QAction(self)
        clearList = QAction(self)

        item = self.filesTreeView.itemAt(point)

        if (item):

            download = QAction(QApplication.translate("MainDialog", "Download Selected", None, QApplication.UnicodeUTF8), self);
            download.setStatusTip(QApplication.translate("MainDialog", "Download the selected packages", None, QApplication.UnicodeUTF8));
            self.connect(download, SIGNAL("triggered()"), self.downloadSelected);

            remove = QAction(QApplication.translate("MainDialog", "Remove Selected", None, QApplication.UnicodeUTF8), self);
            remove.setStatusTip(QApplication.translate("ConvertDialog", "Remove the selected packages from list", None, QApplication.UnicodeUTF8));
            self.connect(remove, SIGNAL("triggered()"), self.removeSelectedFromList);

            clearList = QAction(QApplication.translate("MainDialog", "Clear List", None, QApplication.UnicodeUTF8), self);
            clearList.setStatusTip(QApplication.translate("ConvertDialog", "Remove all packages from the list", None, QApplication.UnicodeUTF8));
            self.connect(clearList, SIGNAL("triggered()"), self.removeAllFromList);

            contextMenu = QMenu(self)
            contextMenu.addAction(download)
            contextMenu.addAction(remove)
            contextMenu.addAction(clearList)

            contextMenu.exec_(QCursor.pos())

    def verify(self):

        """
        Verify if some checkboxes are checked
        """
        self.downloadDefault =  self.downloadDefaultcheckBox.isChecked();
        self.useCache = self.useCachecheckBox.isChecked();

    def updateBar(self):

        """
        Update the status bar.
        """

        self.bar.showMessage("Some Packages weren't listed because they are shipped by default with Ubuntu!")


    def errorListing(self, package):

        "Show a error message if the package doesn't exist"

        message = """Error getting information about package %s!
Verify the package name and try again.
""" %(package)

        QMessageBox.warning(
                self, "USPC",
                QApplication.translate("MainDialog", str(message),
                                       None,
                                       QApplication.UnicodeUTF8)
                )

    def closeOrCancel(self):

        if self.listing:
            self.listThread.terminate()
            self.endFetchingList()
        elif self.downloading:
            self.stopDownloadThreads()
            self.downloadsFinished()
            self.setCanceled()
        else:
            self.close()

    def setCanceled(self):

        "Update the package list setting canceled to all packages"

        count = self.filesTreeView.topLevelItemCount()
        item = QTreeWidgetItem()
        topLevelItem = self.filesTreeView.topLevelItem

        status = QApplication.translate("MainDialog", "Cancelled",
                                       None,
                                       QApplication.UnicodeUTF8)

        downloaded = QApplication.translate("MainDialog", "Downloaded",
                                       None,
                                       QApplication.UnicodeUTF8)

        for i in xrange(count):
            item = topLevelItem(i)
            if item.text(4) != downloaded:
                item.setText(4, status)

    def setOptions(self):

        """Opens the options dialog"""
        
        options = OptionsDialog(self);
        self.connect( options, SIGNAL( "ok()" ), self.readSettings);
        options.exec_();
        
    def readSettings(self):
        """Reads all the settings and populates the Main window widgets"""
            
        defaultOutputDir = QVariant(QDir.homePath())
        
        settings = QSettings("USPC")
        
        settings.beginGroup("USPC")
              
        self.outputFolderEdit.setText(settings.value("defaultOutputFolder", defaultOutputDir).toString())
        self.archComboBox.setCurrentIndex(settings.value("defaultArchiteture", QVariant(ARCH_INDEX)).toInt()[0]) 
        self.distributionComboBox.setCurrentIndex(settings.value("defaultDist", QVariant(0)).toInt()[0])
        self.releaseComboBox.setCurrentIndex(settings.value("defaultVersion", QVariant(0)).toInt()[0])
        self.useCachecheckBox.setChecked(settings.value("usePackageCache", QVariant(False)).toBool())
        self.downloadDefaultcheckBox.setChecked(settings.value("listDefaultPackages", QVariant(False)).toBool())

        settings.endGroup()
